package cn.blnp.net.flowable.boot.customer.flowable.handler;

import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.calendar.BusinessCalendarImpl;
import org.flowable.common.engine.impl.runtime.ClockReader;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.LocalDateTime;
import org.joda.time.Period;

import java.util.Date;

/**
 * <h3>自定义业务日历</h3>
 *
 * @author <a href="mailto:blnp.yibin@qq.com">lyb</a>
 * @version 1.0
 * @since 2025/6/13 11:48
 */
@Slf4j
public class CusBusinessCalendar extends BusinessCalendarImpl {

    public static final String NAME = "custom";

    public CusBusinessCalendar(ClockReader clockReader) {
        super(clockReader);
    }

    @Override
    public Date resolveDuedate(String dueDate, int maxIterations) {
        try {
            if (dueDate.startsWith("P")) {
                //获取当前时间
                LocalDateTime currentTime = LocalDateTime.now();
                LocalDateTime startTime = null;
                LocalDateTime endTime = null;
                //周六处理
                if (currentTime.getDayOfWeek() == 6) {
                    //声明任务当天处理的时间是早上9点到18点
                    startTime = getLocalDateTime(currentTime.plusDays(2), 9, 0, 0);
                    endTime = getLocalDateTime(currentTime.plusDays(2), 18, 0, 0);
                }
                //周天处理
                else if (currentTime.getDayOfWeek() == 7) {
                    startTime = getLocalDateTime(currentTime.plusDays(1), 9, 0, 0);
                    endTime = getLocalDateTime(currentTime.plusDays(1), 18, 0, 0);
                } else {
                    //工作日计算
                    startTime = getLocalDateTime(currentTime, 9, 0, 0);
                    endTime = getLocalDateTime(currentTime, 18, 0, 0);
                }
                LocalDateTime effectiveStartTime = null;
                if (currentTime.toDate().before(startTime.toDate())) {
                    effectiveStartTime = startTime;
                } else if (currentTime.toDate().after(startTime.toDate())
                        && currentTime.toDate().before(endTime.toDate())) {
                    effectiveStartTime = currentTime;
                } else if (currentTime.toDate().after(endTime.toDate())) {
                    effectiveStartTime = startTime.plusDays(1);
                    while (effectiveStartTime.getDayOfWeek() == 6
                            || effectiveStartTime.getDayOfWeek() == 7) {
                        effectiveStartTime = effectiveStartTime.plusDays(1);
                    }
                }
                Duration totalDuration =
                        new Duration(effectiveStartTime.toDateTime(),
                                (getLocalDateTime(effectiveStartTime, 18, 0, 0)).toDateTime());
                Duration duedateDuration = Period.parse(dueDate).toStandardDuration();
                if (totalDuration.isLongerThan(duedateDuration)) {
                    return effectiveStartTime.plus(Period.parse(dueDate)).toDate();
                } else {
                    LocalDateTime nextDay = effectiveStartTime;
                    while(true) {
                        nextDay = nextDay.plusDays(1);
                        if (nextDay.getDayOfWeek() == 6 || nextDay.getDayOfWeek() == 7) {
                            continue;
                        }
                        Duration nextDayDuration = new Duration(
                                getLocalDateTime(nextDay, 9, 0, 0).toDateTime(),
                                (getLocalDateTime(nextDay, 18, 0,0)).toDateTime());
                        if (totalDuration.plus(nextDayDuration)
                                .isShorterThan(duedateDuration)) {
                            totalDuration = totalDuration.plus(nextDayDuration);
                        } else {
                            return getLocalDateTime(nextDay, 9, 0, 0).plus(
                                    duedateDuration.minus(totalDuration)).toDate();
                        }
                    }
                }
            }
            return DateTime.parse(dueDate).toDate();
        } catch (Exception e) {
            throw new FlowableException("couldn't resolve dueDate: " + e.getMessage(), e);
        }
    }

    private LocalDateTime getLocalDateTime(LocalDateTime dateTime, int hourOfDay, int minuteOfHour, int secondOfMinute) {
        return new LocalDateTime(dateTime.getYear(), dateTime.getMonthOfYear(),
                dateTime.getDayOfMonth(), hourOfDay, minuteOfHour, secondOfMinute);
    }
}
